﻿using System;
using System.IO;
using System.Security.Cryptography;

namespace BasicEncrypt_DecryptFiles.Code
{
    public enum AlgProvider : int
    {
        // Supported algorithm
        Rijndael,
        RC2,
        DES,
        TripleDES
    }

    public enum ModeProvider : int
    {
        // Supported modes of operation
        ECB,
        CBC,
        CFB,
        OFB,
        CTS
    }

    public enum PaddingProvider : int
    {
        // Supported padding mode
        X923,
        PKCS7,
        ISO10126
    }

    class SimpleCryptoAlg
    {
        //http://www.codeproject.com/KB/security/DotNetCrypto.aspx;
        //http://courses.fit.hcmus.edu.vn/mod/forum/discuss.php?d=63747

        //private AlgProvider mAlgorithm;
        private SymmetricAlgorithm mCryptoService;
        private byte[] dummyByte = new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76};


        private bool isCompability(AlgProvider iAlg, ModeProvider iMode, PaddingProvider iPadding)
        {
            switch (iAlg)
            {
                case AlgProvider.DES:
                case AlgProvider.Rijndael:
                case AlgProvider.RC2:
                    if (iMode == ModeProvider.OFB || iMode == ModeProvider.CTS)
                        return false;
            	    break;
            }
            return true;
        }

        private void initAlg( AlgProvider iAlg, String strPassword, String IV,
            ModeProvider iMode, PaddingProvider iPadding)
        {
            PasswordDeriveBytes pdbPass = new PasswordDeriveBytes(strPassword, dummyByte);
            PasswordDeriveBytes pdbIV = new PasswordDeriveBytes(IV, dummyByte);

            switch (iAlg)
            {
                case AlgProvider.Rijndael:
                    mCryptoService = new RijndaelManaged();
                    mCryptoService.Key = pdbPass.GetBytes(16);
                    mCryptoService.IV = pdbIV.GetBytes(16);
                    break;
                case AlgProvider.DES:
                    mCryptoService = new DESCryptoServiceProvider();
                    mCryptoService.Key = pdbPass.GetBytes(8);
                    mCryptoService.IV = pdbIV.GetBytes(8);
                    break;
                case AlgProvider.RC2:
                    mCryptoService = new RC2CryptoServiceProvider();
                    mCryptoService.Key = pdbPass.GetBytes(8);
                    mCryptoService.IV = pdbIV.GetBytes(8);
                    break;
                case AlgProvider.TripleDES:
                    mCryptoService = new TripleDESCryptoServiceProvider();
                    mCryptoService.Key = pdbPass.GetBytes(21);
                    mCryptoService.IV = pdbIV.GetBytes(8);
                    break;
            }
            switch (iMode)
            {
                case ModeProvider.ECB:
                    mCryptoService.Mode = CipherMode.ECB;
                    break;
                case ModeProvider.CFB:
                    mCryptoService.Mode = CipherMode.ECB;
                    break;
                case ModeProvider.CBC:
                    mCryptoService.Mode = CipherMode.ECB;
                    break;
                case ModeProvider.OFB:                    
                    mCryptoService.Mode = CipherMode.ECB;
                    break;
                case ModeProvider.CTS:
                    mCryptoService.Mode = CipherMode.ECB;
                    break;
            }

            switch (iPadding)
            {
                case PaddingProvider.ISO10126:
                    mCryptoService.Padding = PaddingMode.ISO10126;
                    break;
                case PaddingProvider.PKCS7:
                    mCryptoService.Padding = PaddingMode.PKCS7;
                    break;
                case PaddingProvider.X923:
                    mCryptoService.Padding = PaddingMode.ANSIX923;
                    break;
            }
        }

        public int encryptFile(String strFileIn, String strFileout, 
            AlgProvider iAlg, String strPassword, String IV, 
            ModeProvider iMode, PaddingProvider iPadding)
        {
            if (!isCompability(iAlg, iMode, iPadding))
                return 1;

            initAlg(iAlg, strPassword, IV, iMode, iPadding);

            FileStream fsIn = new FileStream(strFileIn,
            FileMode.Open, FileAccess.Read);

            FileStream fsOut = new FileStream(strFileout,
                        FileMode.OpenOrCreate, FileAccess.Write);

            if (!fsIn.CanRead || !fsOut.CanWrite)
                return 2;

            CryptoStream cs = new CryptoStream(fsOut, mCryptoService.CreateEncryptor(), CryptoStreamMode.Write);
            ICryptoTransform ac = mCryptoService.CreateEncryptor();
            

            int bufferLen = 4096;
            byte[] buffer = new byte[bufferLen]; 
            int bytesRead; 

            do { 
                // read a chunk of data from the input file 

                bytesRead = fsIn.Read(buffer, 0, bufferLen); 

                // Encrypt it 

                cs.Write(buffer, 0, bytesRead); 

            } while(bytesRead != 0);

            cs.Close();
            fsIn.Close();
            fsOut.Close();
            return 3;
        }

        public int decryptFile(String strFileIn, String strFileout,
            AlgProvider iAlg, String strPassword, String IV,
            ModeProvider iMode, PaddingProvider iPadding)
        {
            if (!isCompability(iAlg, iMode, iPadding))
                return 1;

            initAlg(iAlg, strPassword, IV, iMode, iPadding);

            FileStream fsIn = new FileStream(strFileIn,
            FileMode.Open, FileAccess.Read);

            FileStream fsOut = new FileStream(strFileout,
                        FileMode.OpenOrCreate, FileAccess.Write);

            if (!fsIn.CanRead || !fsOut.CanWrite)
                return 2;

            CryptoStream cs = new CryptoStream(fsOut, mCryptoService.CreateDecryptor(), CryptoStreamMode.Write);

            int bufferLen = 4096;
            byte[] buffer = new byte[bufferLen];
            int bytesRead;

            do
            {
                // read a chunk of data from the input file 

                bytesRead = fsIn.Read(buffer, 0, bufferLen);

                // Decrypt it 

                cs.Write(buffer, 0, bytesRead);

            } while (bytesRead != 0);

            //close everything
            cs.Close();
            fsIn.Close();
            fsOut.Close();

            return 3;
        }
    }

}
